package org.iori.util.text;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

/**
 * The util class to process string.
 * 
 * @author zhang_jianbin
 * @history
 *         <p>
 *         2006-4-22 Created Zhang Jianbin
 * 
 */
public class StringUtil {

	/**
	 * Returns the escaped string.
	 * 
	 * @param value
	 *            the original string
	 * @param pattern
	 *            the escape pattern.<br>
	 *            The following patterns are support:<br>
	 *            <ul>
	 *            <li>date string conversion,YYYY/MM/DD,YYYY/MM
	 *            <li>code to value,{value|escaped value,...value|escaped
	 *            value,}
	 *            </ul>
	 * @return the escaped value, if the value is null, an empty string is
	 *         returned.<br>
	 *         if the pattern is not support, the original value is returned.
	 * 
	 */
	public static String escapeValue(String value, String pattern) {
		return escape(value, "", pattern);
	}

	private static String escape(String value, String lastValue, String pattern) {
		String returnValue = value;
		if (pattern.equals("YYYY/MM/DD")) {
			if (null == value) {
				returnValue = "";
			} else {
				value = value.trim();
				try {
					SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
					sdf.setLenient(false);
					if (null == sdf.parse(value)) {
						return value;
					}
				} catch (ParseException e) {
					return value;
				}

				returnValue = value.substring(0, 4) + "/"
						+ value.substring(4, 6) + "/" + value.substring(6, 8);

			}

		} else if (pattern.equals("YYYY/MM")) {
			if (null == value) {
				returnValue = "";
			} else {
				value = value.trim();
				try {
					SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
					sdf.setLenient(false);
					if (null == sdf.parse(value)) {
						return value;
					}
				} catch (ParseException e) {
					return value;
				}

				returnValue = value.substring(0, 4) + "/"
						+ value.substring(4, 6);

			}

		} else if (pattern.startsWith("{") && pattern.endsWith("}")) {
			String[] items = StringUtil.splitString(pattern.substring(1,
					pattern.length() - 1), ",");

			boolean found = false;
			for (int i = 0; i < items.length; i++) {
				String[] item = items[i].split("[|]");

				String itemValue = item.length == 2 ? item[1] : "";
				String itemCondition = item[0];

				if (value != null && 0 != value.trim().length()) {
					if (itemCondition.equals(value.trim())) {
						found = true;
						returnValue = escape(value, itemValue, itemValue);
						break;
					}
				}
			}

			if (!found) {
				returnValue = value;
			}
		} else {
			returnValue = lastValue;
		}

		return returnValue;
	}

	/**
	 * Relace all the tags in the StringBuffer object.
	 * 
	 * @param sb
	 *            the StringBuffer object, required
	 * @param from
	 *            the tag string for search, ie.{tag name}, {1}, required
	 * @param to
	 *            the replacement string, cannot be null but may be empty
	 * @return the StringBuffer passed into the method
	 */
	public static StringBuffer replaceTag(StringBuffer sb, String from,
			String to) {
		if (null == sb || null == from || null == to
				|| from.trim().length() == 0) {
			return sb;
		}

		int pos = 0;
		while ((pos = sb.indexOf(from, pos)) != -1) {
			sb.replace(pos, pos + from.trim().length(), to);
			pos += from.trim().length();
		}

		return sb;
	}

	/**
	 * Trims the string even if the string is null
	 * 
	 * @param str
	 *            the string
	 * @return the trimed string, an empty string is returned if the value is
	 *         null
	 */
	public static String trimString(String str) {
		if (str == null) {
			return "";
		}

		return str.trim();
	}

	/**
	 * Splits the string.
	 * 
	 * @param str
	 *            the string value
	 * @param regex
	 *            the replacement regular expression
	 * @param limit
	 *            the limit value
	 * @return the splited string array, if the string is null or empty(after
	 *         trim), returns zero-length array
	 */
	public static String[] splitString(String str, String regex, int limit) {
		if (null == str) {
			return new String[0];
		}

		String[] items = str.split(regex, limit);
		if (1 == items.length && "".equals(items[0])) {
			return new String[0];
		}

		return items;
	}

	/**
	 * Splits the string.
	 * 
	 * @param str
	 *            the string value
	 * @param regex
	 *            the replacement regular expression
	 * @return the splited string array, if the string is null or empty(after
	 *         trim), returns zero-length array
	 */
	public static String[] splitString(String str, String regex) {
		if (null == str) {
			return new String[0];
		}

		String[] items = str.split(regex);
		if (1 == items.length && "".equals(items[0])) {
			return new String[0];
		}

		return items;
	}

	/**
	 * Merges each element of the String array to a single string, separated by
	 * the specified delimiter.
	 * 
	 * @param items
	 *            the String array
	 * @param itemDelim
	 *            the delimiter used between each item.
	 * @return the single string that each item is separated by the delimiter
	 *         characaters. If the element of the String array is null, then
	 *         this item is only a placehold.
	 */
	public static String merge2String(String[] items, String itemDelim) {
		if (null == items || null == itemDelim) {
			return "";
		}

		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < items.length; i++) {
			if (null != items[i]) {
				sb.append(items[i]);
			}

			if (i != items.length - 1) {
				sb.append(itemDelim);
			}
		}

		return sb.toString();
	}

	/**
	 * Merges each element of the Map object to a single string, separated by
	 * the specified delimiter.
	 * 
	 * @param map
	 *            the Map object
	 * @param itemDelim
	 *            the delimiter used between each item.
	 * @return the single string that each item is separated by the delimiter
	 *         characaters. The delimiter between key and value is charactaer
	 *         "=". If the value of the key in the map is null, then this value
	 *         is set to empty.
	 */
	public static String merge2String(Map map, String itemDelim) {
		Iterator iterator = map.entrySet().iterator();
		StringBuffer sb = new StringBuffer(64);
		while (iterator.hasNext()) {
			Entry entry = (Entry) iterator.next();
			String key = (String) entry.getKey();
			String value = "";
			if (entry.getValue() instanceof String) {
				value = (String) entry.getValue();
			}

			sb.append(key);
			sb.append("=");
			sb.append(value == null ? "" : value);
			if (iterator.hasNext()) {
				sb.append(itemDelim);
			}
		}
		return sb.toString();
	}

	/**
	 * Merges each element of the Map object to a single string, separated by
	 * the specified delimiter.
	 * 
	 * @param map
	 *            the Map object
	 * @param keys
	 *            the String array of the speicified keys by order.
	 * @param itemDelim
	 *            the delimiter used between each item.
	 * @return the single string that each item is separated by the delimiter
	 *         characaters. The delimiter between key and value is charactaer
	 *         "=". If the value of the key in the map is null, then this value
	 *         is set to empty.
	 */
	public static String merge2String(Map map, String[] keys, String itemDelim) {
		return merge2String(map, keys, "=", itemDelim);
	}

	/**
	 * Merges each element of the Map object to a single string, separated by
	 * the specified delimiter.
	 * 
	 * @param map
	 *            the Map object
	 * @param keys
	 *            the String array of the speicified keys by order.
	 * @param keyDelim
	 *            the delimiter used between key and value of the map.
	 * @param itemDelim
	 *            the delimiter used between each item
	 * @return the single string that each item is separated by the delimiter
	 *         characaters. If the value of the key in the map is null, then
	 *         this value is set to empty. The value is not trimed.
	 */
	public static String merge2String(Map map, String[] keys, String keyDelim,
			String itemDelim) {
		return merge2String(map, keys, keyDelim, itemDelim, false, null, null);
	}

	/**
	 * Merges each element of the Map object to a single string, separated by
	 * the specified delimiter.
	 * 
	 * @param map
	 *            the Map object
	 * @param keys
	 *            the String array of the speicified keys by order.
	 * @param keyDelim
	 *            the delimiter used between key and value of the map.
	 * @param itemDelim
	 *            the delimiter used between each item
	 * @param trimValue
	 *            indicates whether the value of the key is trimed or not before
	 *            merging to the string.
	 * @param matchPattern
	 *            If the value matches the regular pattern, then the matched
	 *            parts will be replaced by the replacement string
	 * @param replacement
	 *            the replacement, it is required when matchPattern is exist
	 * @return the single string that each item is separated by the delimiter
	 *         characaters. If the value of the key in the map is null, then
	 *         this value is set to empty.
	 */
	public static String merge2String(Map map, String[] keys, String keyDelim,
			String itemDelim, boolean trimValue, String matchPattern,
			String replacement) {
		StringBuffer sb = new StringBuffer();

		for (int i = 0; i < keys.length; i++) {
			Object object = map.get(keys[i]);

			String value = "";
			if (null != object && object instanceof String) {
				value = (String) object;
			}

			if (null != matchPattern && 0 != matchPattern.trim().length()
					&& null != replacement) {
				value = value.replaceAll(matchPattern, replacement);
			}

			if (trimValue) {
				value = value.trim();
			}

			sb.append(keys[i]);
			sb.append(keyDelim);
			sb.append(value);
			if (i != keys.length - 1) {
				sb.append(itemDelim);
			}
		}
		return sb.toString();
	}

	/**
	 * Returns the not-null value of the map by the key.
	 * 
	 * @param map
	 *            the map object
	 * @param key
	 *            the key name
	 * @return If the value of the key is null or is not a String object, return
	 *         empty String. Otherwise a trimed value is returned.
	 */
	public static String getNotNullValue(Map map, String key) {
		Object object = map.get(key);
		if (null == object || !(object instanceof String)) {
			return "";
		}

		return ((String) object).trim();
	}

	/**
	 * Returns the not-null value of the String.
	 * 
	 * @param str
	 *            the String value
	 * @return returns an empty String if the value of the String is null.
	 *         Otherwise a trimed value is returned.
	 */
	public static String getNotNullValue(String str) {
		if (null == str) {
			return "";
		}

		return str.trim();
	}

	/**
	 * Returns the position that the character at which is the first
	 * non-whitespace from the right side. If no such position, returns -1;
	 * 
	 * @param sb
	 *            the StringBuffer object
	 * @return the right trim postion
	 */
	public static int getRTrimPosition(StringBuffer sb) {
		if (null == sb || sb.length() == 0) {
			return -1;
		}

		int pos = sb.length() - 1;
		while (pos >= 0 && Character.isWhitespace(sb.charAt(pos))) {
			pos--;
		}
		return pos;
	}

	/**
	 * Returns the position that the character at which is the first
	 * non-whitespace from the left side. If no such position, returns -1;
	 * 
	 * @param sb
	 *            the StringBuffer object
	 * @return the left trim position
	 */
	public static int getLTrimPosition(StringBuffer sb) {
		if (null == sb || sb.length() == 0) {
			return 0;
		}

		int pos = 0;
		while (pos < sb.length() && Character.isWhitespace(sb.charAt(pos))) {
			pos++;
		}

		// no such character
		if (pos == sb.length()) {
			return -1;
		}

		return pos;
	}

	/**
	 * Returns the formated string with the java method name conventional: <br>
	 * 1.The first word is converted to lowercase format <br>
	 * 2.The first character of the other words is uppercase <br>
	 * 3.The '_' character in the each word is treated as the words seprator.
	 * 
	 * @param words
	 *            the word string
	 * @return the formated string
	 */
	public static String formart2JavaMethodName(String words) {
		if (null == words) {
			return null;
		}

		StringBuffer sb = new StringBuffer();

		String[] token = StringUtil.splitString(words, "_");

		// First letter to uppercase, the others remain
		for (int i = 0; i < token.length; i++) {
			if (token[i].trim().length() == 0) {
				continue;
			}

			// the first word is converted to lowercase.
			if (sb.length() == 0) {
				sb.append(token[i].toLowerCase());
				continue;
			}

			sb.append(token[i].substring(0, 1).toUpperCase());
			sb.append(token[i].substring(1).toLowerCase());
		}

		return sb.toString();
	}

	/**
	 * Filters the String value to show it the HTML element.
	 * 
	 * @param value
	 * @return the filterd value. If the value is null, empty String is
	 *         returned.
	 */
	public static String htmlFilter(String value) {
		if (null == value || value.length() == 0) {
			return "&nbsp;";
		}

		return htmlFilter(new StringBuffer(value)).toString();
	}

	/**
	 * Filter the special characters to the HTML format. <br>
	 * The following are the replacement mapping. <br>
	 * Space Character(&nbsp;), <(&lt;),>(&gt;),\r(ignore),\n( <br>)
	 * 
	 * @param sb
	 *            the string buffer that contains original value
	 * @return the same string buffer that contains the filtered string
	 */
	public static StringBuffer htmlFilter(StringBuffer sb) {
		int currLength = sb.length();
		int escapeChars = 0;
		for (int i = currLength - 1; i >= 0; i--) {
			switch (sb.charAt(i)) {
			case ' ':
				sb.replace(i, i + 1, "&nbsp;");
				escapeChars++;
				break;
			case '<':
				sb.replace(i, i + 1, "&lt;");
				escapeChars++;
				break;
			case '>':
				sb.replace(i, i + 1, "&gt;");
				escapeChars++;
				break;
			case '\r':
				sb.deleteCharAt(i);
				break;
			case '\n':
				sb.replace(i, i + 1, "<br>");
				escapeChars++;
				break;
			}
		}

		return sb;
	}
}